#include "Scene_PCH.h"
#include "bTerrain.h"

/*--------------------------------------------------------------------------*/

bTerrain::bTerrain(btDiscreteDynamicsWorld* world, const bInitData& initdata, const TerrainInit& terraindata) :
	bObject(world),
	m_heightmap(NULL),
	m_size(-1),
	m_flags(0)
{
	m_quadCenterPosition = glm::vec2(initdata.position.x, initdata.position.z);
	m_collisionShape = GenerateHeightmap(terraindata);
	__Check(m_collisionShape);

	GenerateMesh(initdata);
	AddRigidBody(m_collisionShape, initdata);

	m_material = NULL;
}

/*--------------------------------------------------------------------------*/

bTerrain::~bTerrain()
{
	RemoveRigidBody();
	delete [] m_heightmap;
}

/*--------------------------------------------------------------------------*/
const RMaterial* matter = NULL;
void bTerrain::PreRendering( uint arg )
{
	if (matter == NULL)
	{
		MaterialDesc md;
		md.m_flags = EMaterialDesc::ColorAsTexture0 | EMaterialDesc::OutNormal | EMaterialDesc::UseBumpmapping;
		md.m_blendstate = g_Renderer->GetRendererStorageMgr()->GetBlendState("terrain");
		md.m_rasterizerstate = g_Renderer->GetRendererStorageMgr()->GetRasterizerState("terrain");
		md.m_depthstencilstate = g_Renderer->GetRendererStorageMgr()->GetDepthstencilState("terrain");
		md.m_Bumpmapping_normal0 = g_Renderer->GetRendererStorageMgr()->GetTexture("normal");
		md.m_Bumpmapping_TextureMultiplier = 200.0f;
		md.m_texture0 = g_Renderer->GetRendererStorageMgr()->GetTexture("sand1");
		
		m_material = g_Renderer->GetMaterialMgr()->GetMaterialFromMaster(md);
		matter = m_material;
	}

	m_material = matter;

	switch (arg)
	{
	case 0:
		{
			g_Renderer->GetQueueMgr()->AddTask(this, m_material, m_vertexarray, ETopology::TriangleStrip, m_vertexarray->GetNumIndices(), 0);
			break;
		}
	}
}

/*--------------------------------------------------------------------------*/

btHeightfieldTerrainShape* bTerrain::GenerateHeightmap( const TerrainInit& terraindata )
{
	__Check(m_heightmap == NULL);
	__Check(m_size == -1);

	m_size = terraindata.m_size;
	m_heightmap = new float[(m_size)*(m_size)];

	bool onlyZeros = true;

	for (int x = 0; x < m_size; ++x)
	{
		for (int y = 0; y < m_size; ++y)
		{
			m_heightmap[x + m_size * y] = GetHeight(x, y);

			if (m_heightmap[x + m_size * y] != 0)
			{
				onlyZeros = false;
			}

			__Check(m_heightmap[x + m_size * y] >= terraindata.m_minHeight);
			__Check(m_heightmap[x + m_size * y] <= terraindata.m_maxHeight);
		}
	}

	if (onlyZeros)
	{
		m_flags |= ETerrainFlags::OnlyZeros;
	}

	return new btHeightfieldTerrainShape(m_size, m_size, m_heightmap, 1.0f, terraindata.m_minHeight, terraindata.m_maxHeight, 1, PHY_FLOAT, true);
}

/*--------------------------------------------------------------------------*/

void bTerrain::GenerateMesh( const bInitData& initdata )
{
	__Check(m_heightmap);
	__Check(m_size > 0);

	TerrainSurface* surface = new TerrainSurface(std::to_string(initdata.position.x) + " " + std::to_string(initdata.position.z), m_size);

	glm::vec3* position0	= surface->vPosition0();
	glm::vec3* normal0		= surface->vNormal0();
	glm::vec2* texcoord0	= surface->vTexcoord0();
	ushort* indices			= surface->GetIndices();

	int width = m_size;
	int height = m_size;

	for (int x = 0; x < width; ++x)
	{
		for (int y = 0; y < height; ++y)
		{
			int id = x + m_size * y;
			position0[id]	= glm::vec3(x - (m_size/2), m_heightmap[x + m_size * y], y - (m_size/2));
			texcoord0[id]	= glm::vec2(float(x) / float(width-1), float(y) / float(height-1));	
		}
	}

	for (int x = 0; x < width; ++x)
	{
		for (int y = 0; y < height; ++y)
		{
			int id = x + m_size * y;
			int xa = glm::clamp(x-1, 0, width-1);
			int xb = glm::clamp(x+1, 0, width-1);
			int ya = glm::clamp(y-1, 0, height-1);
			int yb = glm::clamp(y+1, 0, height-1);

			glm::vec3 vecX	= position0[ xa + m_size * y ] - position0[ xb + m_size * y ];
			glm::vec3 vecY	= position0[ x + m_size * ya ] - position0[ x + m_size * yb ];
			normal0[id]		= -glm::cross(glm::normalize(vecX), glm::normalize(vecY));

			//__Info("%f %f %f", normal0[id].x, normal0[id].y, normal0[id].z);
		}
	}
	
	int c = 0;

	for (int row = 0; row < height - 1; ++row)
	{
		for (int col = 0; col < width; ++col)
		{
			indices[c++] = col + row * width;
			indices[c++] = col + (row + 1) * width;
		}

		indices[c++] = 0xffff;
	}

	g_Renderer->AddRequest((void**) &m_vertexarray, new VertexArrayRequest(surface));
}

/*--------------------------------------------------------------------------*/

float bTerrain::GetHeight( int x, int y )
{
	//local coords to hmap coords
	glm::vec2 position = m_quadCenterPosition + glm::vec2(x - (m_size/2), y - (m_size/2));
	return g_Scene->GetWorldMgr()->GetLoadedLevel()->GetHeight(position.x, position.y);

}

/*--------------------------------------------------------------------------*/

void bTerrain::WriteModelMatrix( float* modelmatrix ) const
{
	btTransform transform;
	m_rigidBody->getMotionState()->getWorldTransform(transform);
	transform.getOpenGLMatrix(modelmatrix);
}

/*--------------------------------------------------------------------------*/
